﻿using ArielCore.MixedDataBase.Extensions;
using DotNetCore.CAP;
using Hangfire;
using Hangfire.AspNetCore;
using Hangfire.Dashboard;
using Masuit.Tools.Config;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using NewRetail.Application.Components.TianQue.Configurations;
using NewRetail.Application.Components.Warehouse;
using NewRetail.Application.Components.WeChat.Configurations;
using NewRetail.Application.Components.WeChat.Services.HttpClients;
using NewRetail.Application.Core;
using NewRetail.Application.Core.AutoMapprConfig;
using NewRetail.Application.Core.Entity;
using NewRetail.Application.DataAccess;
using NewRetail.Application.Worker;
using Serilog;
using Serilog.Events;
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using Volo.Abp;
using Volo.Abp.Auditing;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Modularity;
using Volo.Abp.Timing;
using Volo.Abp.Validation;

namespace NewRetail.Application
{
    public class MyDashboardAuthorizationFilter : IDashboardAuthorizationFilter
    {
        public bool Authorize([NotNull] DashboardContext context)
        {
            return true;
        }
    }

    [DependsOn(
        typeof(Volo.Abp.EntityFrameworkCore.SqlServer.AbpEntityFrameworkCoreSqlServerModule),
        //typeof(Volo.Abp.Dapper.AbpDapperModule),
        typeof(EasyAbp.Abp.EventBus.Cap.AbpEventBusCapModule),
        typeof(Volo.Abp.BlobStoring.FileSystem.AbpBlobStoringFileSystemModule),
        typeof(Volo.Abp.Caching.StackExchangeRedis.AbpCachingStackExchangeRedisModule),
        typeof(Volo.Abp.Http.Client.AbpHttpClientModule),
        typeof(Volo.Abp.BackgroundJobs.Hangfire.AbpBackgroundJobsHangfireModule),
        typeof(Volo.Abp.Application.AbpDddApplicationModule)
        )]
    public class NewRetailModule : AbpModule
    {
        void ConfigureDataAccess(ServiceConfigurationContext context)
        {
            context.Services.AddAbpDbContext<NewRetailDbContext>(option =>
            {
                option.AddDefaultRepositories(includeAllEntities: true);
            });

            this.Configure<AbpDbContextOptions>(option =>
            {
                option.Configure<NewRetailDbContext>(context_option =>
                {
                    context_option.UseSqlServer();
                    context_option.DbContextOptions.EnableDetailedErrors();
                    context_option.DbContextOptions.EnableSensitiveDataLogging();
                });
            });
            //添加dapper add 20240223 by cl
            var config = context.Services.GetConfiguration();
            context.Services.AddCoreDbContext<NewRetailDapperDbContext>(config.GetSection("ConnectionStrings")["new_retail"], DataBaseType.SqlServer);
        }

        void ConfigureHangfire(ServiceConfigurationContext context)
        {
            var client = context.Services.GetSingletonInstance<RedisClient>();
            context.Services.AddHangfire((provider, config) =>
            {
                config.UseRedisStorage(
                    connectionMultiplexer: client.Connection,
                    options: new Hangfire.Redis.RedisStorageOptions()
                    {
                        Prefix = "new-retail",
                        Db = 3
                    });

                config.UseActivator(new AspNetCoreJobActivator(provider.GetRequiredService<IServiceScopeFactory>()));
                config.UseLogProvider(new AspNetCoreLogProvider(provider.GetRequiredService<ILoggerFactory>()));
                config.UseFilter(new AutomaticRetryAttribute() { Attempts = 5 });
            });
        }

        void ConfigureDotnetCap(ServiceConfigurationContext context)
        {
            var client = context.Services.GetSingletonInstance<RedisClient>();
            context.Services.AddCap(option =>
            {
                option.UseRedis(connection: client.ConnectionString);
                option.UseInMemoryStorage();

                option.UseDashboard(x =>
                {
                    x.PathMatch = "/internal/cap";
                });
            });
        }

        void ConfigureLogging(ServiceConfigurationContext context)
        {
            var config = context.Services.GetConfiguration();

            LoggerConfiguration CreateConfiguration() => new Serilog.LoggerConfiguration()
                .ReadFrom.Configuration(config, sectionName: "Serilog");

            context.Services.AddLogging(builder =>
            {
                builder.AddDebug();
                builder.AddConsole();

                var env = context.Services.GetHostingEnvironment();
                var logDir = Path.Combine(env.ContentRootPath, "logs");
                if (!Directory.Exists(logDir))
                {
                    Directory.CreateDirectory(logDir);
                }

                var outputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {SourceContext} {Message:lj}{NewLine}{Exception}";

                var errorLevelType = new[] { LogEventLevel.Error, LogEventLevel.Fatal };

                var logger = CreateConfiguration()
                .Filter.ByIncludingOnly(x => !errorLevelType.Contains(x.Level))
                .WriteTo.File(
                            path: Path.Combine(logDir, "app.log"),
                            outputTemplate: outputTemplate,
                            flushToDiskInterval: TimeSpan.FromSeconds(3),
                            fileSizeLimitBytes: 1024 * 2014 * 100,
                            retainedFileCountLimit: 31,
                            rollOnFileSizeLimit: true,
                            rollingInterval: RollingInterval.Day).CreateLogger();
                builder.AddSerilog(logger);

                var errorLogger = CreateConfiguration()
                .Filter.ByIncludingOnly(x => errorLevelType.Contains(x.Level))
                .WriteTo.File(
                            path: Path.Combine(logDir, "app-error.log"),
                            outputTemplate: outputTemplate,
                            flushToDiskInterval: TimeSpan.FromSeconds(3),
                            fileSizeLimitBytes: 1024 * 2014 * 100,
                            retainedFileCountLimit: 31,
                            rollOnFileSizeLimit: true,
                            rollingInterval: RollingInterval.Day).CreateLogger();
                builder.AddSerilog(errorLogger);
            });
        }

        void ConfigureDataProtection(ServiceConfigurationContext context)
        {
            var connection = context.Services.GetSingletonInstance<RedisClient>().Connection;
            context.Services.AddDataProtection().PersistKeysToStackExchangeRedis(connection);
        }

        void ConfigureEntityCollection(ServiceConfigurationContext context)
        {
            var cls = typeof(NewRetailModule).Assembly.GetTypes()
                .Where(x => x.IsClass && x.IsPublic && !x.IsGenericType)
                .Where(x => x.IsAssignableTo<BaseEntity>())
                .ToArray();

            var dict = cls.ToDictionary(x => x.GetTypeIdentity(), x => x);
            context.Services.AddSingleton(new DataTypeDefinition(dict));
            context.Services.AddTransient(typeof(DataSyncImpl<>));
        }

        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            var config = context.Services.GetConfiguration();
            context.Services.AddSingleton(new RedisClient(config.GetRedisConnectionString()));
            context.Services.AddSingleton<RedLockFactoryWrapper>();

            config.AddToMasuitTools();

            this.ConfigureLogging(context);
            this.ConfigureDataAccess(context);
            this.ConfigureHangfire(context);
            this.ConfigureDotnetCap(context);
            this.ConfigureDataProtection(context);
            this.ConfigureEntityCollection(context);

            context.Services.AddHttpClient();
            //审计日志配置
            this.Configure<AbpAuditingOptions>(option =>
            {
                option.IsEnabled = false;
                option.IsEnabledForGetRequests = false;
            });

            this.Configure<AbpClockOptions>(option => option.Kind = System.DateTimeKind.Local);

            #region add by cl
            // 注入配置项
            context.Services.AddOptions();
            context.Services.Configure<WeChatOptions>(config.GetSection(nameof(WeChatOptions)));
            context.Services.Configure<WxpayOptions>(config.GetSection(nameof(WxpayOptions)));
            //天阙配置项注入
            context.Services.Configure<TianQueOptions>(config.GetSection(nameof(TianQueOptions)));

            //添加对AutoMapper的支持
            context.Services.AddAutoMapper(typeof(AutoMapprProfile));

            #region 在.Net Core 3.0以上，会出现 Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead，添加如下解决
            // If using Kestrel:
            context.Services.Configure<KestrelServerOptions>(options =>
            {
                options.AllowSynchronousIO = true;
            });
            // If using IIS:
            context.Services.Configure<IISServerOptions>(options =>
            {
                options.AllowSynchronousIO = true;
            });
            #endregion

            //注入工厂
            context.Services.AddSingleton<IWeChatApiHttpClientFactory, WeChatApiHttpClientFactory>();
            //context.Services.AddSingleton<CertificateManager, InMemoryCertificateManager>();
            context.Services.AddSingleton<IWechatTenpayCertificateManagerFactory, WechatTenpayCertificateManagerFactory>();
            context.Services.AddSingleton<IWechatTenpayHttpClientFactory, WechatTenpayHttpClientFactory>();
            #endregion
        }

        public override void PostConfigureServices(ServiceConfigurationContext context)
        {
            context.Services.RemoveAll<IMethodInvocationValidator>().AddTransient<IMethodInvocationValidator, NullMethodInvocationValidator>();
            context.Services.RemoveAll<IObjectModelValidator>().AddSingleton<IObjectModelValidator, NullObjectModelValidator>();
        }

        public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            var app = context.GetApplicationBuilder();
            app.UseHangfireServer();
            app.UseHangfireDashboard("/internal/hangfire", new Hangfire.DashboardOptions()
            {
                Authorization = new[] { new MyDashboardAuthorizationFilter() }
            });

            app.UseCapDashboard();

            app.StartWorkers();
        }
    }
}
